#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/pem.h>
#include <stdio.h>
#include <string.h>

// 辅助函数：打印十六进制数据
void print_hex(const char* label, const unsigned char* data, int len) {
    printf("%s: ", label);
    for (int i = 0; i < len && i < 64; i++) {  // 限制打印长度避免过长输出
        printf("%02x", data[i]);
    }
    if (len > 64) printf("...");
    printf(" (长度: %d 字节)\n", len);
}

int sm2_encrypt_decrypt() {
    EVP_PKEY *pkey = NULL;
    EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_SM2, NULL);
    if (!pctx) {
        fprintf(stderr, "创建PKEY_CTX失败\n");
        return -1;
    }

    // 生成 SM2 密钥对
    if (EVP_PKEY_keygen_init(pctx) <= 0) {
        fprintf(stderr, "初始化密钥生成失败\n");
        goto err;
    }
    if (EVP_PKEY_keygen(pctx, &pkey) <= 0) {
        fprintf(stderr, "密钥生成失败\n");
        goto err;
    }
    EVP_PKEY_CTX_free(pctx);
    // 获取公钥和私钥的DER编码以便打印
    unsigned char *pub_key_der = NULL;
    int pub_key_len = i2d_PUBKEY(pkey, &pub_key_der);
    if (pub_key_len > 0) {
        print_hex("公钥 (DER格式)", pub_key_der, pub_key_len);
        OPENSSL_free(pub_key_der);
    }
    
    unsigned char *priv_key_der = NULL;
    int priv_key_len = i2d_PrivateKey(pkey, &priv_key_der);
    if (priv_key_len > 0) {
        print_hex("私钥 (DER格式)", priv_key_der, priv_key_len);
        OPENSSL_free(priv_key_der);
    }

    const char *plaintext_str = "This is 20231318fsl";
    unsigned char plaintext[256];
    size_t plaintext_len = strlen(plaintext_str);
    memcpy(plaintext, plaintext_str, plaintext_len);
    
    printf("\n=== 加密过程 ===\n");
    print_hex("原始明文", plaintext, plaintext_len);

    // 加密
    EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (!ctx) {
        fprintf(stderr, "创建加密CTX失败\n");
        goto err;
    }

    if (EVP_PKEY_encrypt_init(ctx) <= 0) {
        fprintf(stderr, "初始化加密失败\n");
        EVP_PKEY_CTX_free(ctx);
        goto err;
    }

    const char *id = "20231318";
    int id_result = EVP_PKEY_CTX_set1_id(ctx, id, strlen(id));

    size_t ciphertext_len;
    if (EVP_PKEY_encrypt(ctx, NULL, &ciphertext_len, plaintext, plaintext_len) <= 0) {
        fprintf(stderr, "获取加密输出长度失败\n");
        EVP_PKEY_CTX_free(ctx);
        goto err;
    }

    unsigned char *ciphertext = OPENSSL_malloc(ciphertext_len);
    if (!ciphertext) {
        fprintf(stderr, "分配密文缓冲区失败\n");
        EVP_PKEY_CTX_free(ctx);
        goto err;
    }

    if (EVP_PKEY_encrypt(ctx, ciphertext, &ciphertext_len, plaintext, plaintext_len) <= 0) {
        fprintf(stderr, "加密失败\n");
        OPENSSL_free(ciphertext);
        EVP_PKEY_CTX_free(ctx);
        goto err;
    }
    
    print_hex("加密后密文", ciphertext, ciphertext_len);
    EVP_PKEY_CTX_free(ctx);

    printf("\n=== 解密过程 ===\n");
    print_hex("待解密密文", ciphertext, ciphertext_len);

    // 解密
    ctx = EVP_PKEY_CTX_new(pkey, NULL);
    if (!ctx) {
        fprintf(stderr, "创建解密CTX失败\n");
        OPENSSL_free(ciphertext);
        goto err;
    }

    if (EVP_PKEY_decrypt_init(ctx) <= 0) {
        fprintf(stderr, "初始化解密失败\n");
        EVP_PKEY_CTX_free(ctx);
        OPENSSL_free(ciphertext);
        goto err;
    }

    // 设置相同的用户ID（如果加密时设置了）
    id_result = EVP_PKEY_CTX_set1_id(ctx, id, strlen(id));
    if (id_result <= 0) {
        printf("警告: 设置用户ID失败，继续执行解密...\n");
    } else {
        printf("成功设置用户ID用于解密: %s\n", id);
    }

    size_t decrypted_len;
    if (EVP_PKEY_decrypt(ctx, NULL, &decrypted_len, ciphertext, ciphertext_len) <= 0) {
        fprintf(stderr, "获取解密输出长度失败\n");
        EVP_PKEY_CTX_free(ctx);
        OPENSSL_free(ciphertext);
        goto err;
    }

    unsigned char *decrypted = OPENSSL_malloc(decrypted_len);
    if (!decrypted) {
        fprintf(stderr, "分配解密缓冲区失败\n");
        EVP_PKEY_CTX_free(ctx);
        OPENSSL_free(ciphertext);
        goto err;
    }

    if (EVP_PKEY_decrypt(ctx, decrypted, &decrypted_len, ciphertext, ciphertext_len) <= 0) {
        fprintf(stderr, "解密失败\n");
        OPENSSL_free(decrypted);
        EVP_PKEY_CTX_free(ctx);
        OPENSSL_free(ciphertext);
        goto err;
    }
    
    print_hex("解密后明文", decrypted, decrypted_len);
    
    // 验证加解密是否成功
    if (decrypted_len == plaintext_len && memcmp(plaintext, decrypted, decrypted_len) == 0) {
        printf("\n=== 验证结果 ===\n");
        printf("解密成功！明文与原始数据一致。\n");
        
        // 打印可读的明文字符串
        printf("原始字符串: \"%.*s\"\n", (int)plaintext_len, plaintext);
        printf("解密字符串: \"%.*s\"\n", (int)decrypted_len, decrypted);
    } else {
        printf("\n=== 验证结果 ===\n");
        printf("解密失败！明文与原始数据不一致。\n");
    }

    EVP_PKEY_CTX_free(ctx);
    OPENSSL_free(ciphertext);
    OPENSSL_free(decrypted);
    EVP_PKEY_free(pkey);
    return 0;

err:
    if (pctx) EVP_PKEY_CTX_free(pctx);
    if (pkey) EVP_PKEY_free(pkey);
    return -1;
}

int main() {
    if (sm2_encrypt_decrypt() == 0) {
        printf("\n=== 程序执行完成 ===\n");
    } else {
        printf("SM2 加解密失败\n");
        return 1;
    }
    return 0;
}




